/*******************************************************************************
 * Copyright (c) 2011 TXT e-solutions SpA
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 *
 * Authors:
 *     Cristoforo Seccia (TXT e-solutions SpA)
 *
 * Contributors:
 *      Domenico Rotondi (TXT e-solutions SpA)
 *******************************************************************************/
package it.txt.access.capability.demo.schema.response.factory;

import it.txt.access.capability.commons.identifier.IdentifierGenerator;
import it.txt.access.capability.commons.schema.factory.CapabilitySchemasJaxbFaxtory;
import it.txt.access.capability.commons.schema.factory.CapabilitySchemasLoader;
import it.txt.access.capability.commons.utils.CalendarUtils;
import it.txt.access.capability.commons.utils.DOMUtils;
import it.txt.access.capability.demo.schema.CapabilityResponseType;
import it.txt.access.capability.demo.schema.ObjectFactory;
import it.txt.access.capability.demo.schema.factory.CapabilityDemoFactoryException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 *
 * @author Cristoforo Seccia (TXT e-solutions SpA)
 */
public class CapabilityDemoResponseFactory {

    private static final Logger logger = Logger.getLogger(CapabilityDemoResponseFactory.class.getName());
    public static final String JAXB_CONTEXT = "it.txt.access.capability.demo.schema";
    public static final String SIGN_DOCUMENT_AFTER_NODE = "CapabilityResponseID";
    public static final int CAPABILITY_RESPONSE_VERSION = 1;
    private static CapabilityDemoResponseFactory factory;
    private final CapabilitySchemasJaxbFaxtory wrapper;
    private final ObjectFactory responseFactory;

    private CapabilityDemoResponseFactory(String capabilitySchemasRootPath) throws CapabilityDemoFactoryException{
        try {
            CapabilitySchemasLoader loader = new CapabilitySchemasLoader(capabilitySchemasRootPath);
            wrapper = new CapabilitySchemasJaxbFaxtory(
                    loader.loadDemoCapabilityEntities(capabilitySchemasRootPath),
                    CapabilityResponseType.class);
            responseFactory = new ObjectFactory();
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized static CapabilityDemoResponseFactory getInstance(String capabilitySchemasRootPath)
            throws CapabilityDemoFactoryException {
        if(factory == null){
            factory = new CapabilityDemoResponseFactory(capabilitySchemasRootPath);
        }
        return factory;
    }

    /**
     * Warning! Use this method only if the CapabilitySchemaFactory has been
     * initialized with one of the parameterized 'getInstance(...)' method.
     */
    public synchronized static CapabilityDemoResponseFactory getInstance() throws CapabilityDemoFactoryException {
        if(factory == null){
            throw new CapabilityDemoFactoryException("CapabilityDemoResponseFactory has not been initialized");
        }
        return factory;
    }

    public synchronized CapabilityResponseType createResponse(Source source) throws CapabilityDemoFactoryException {

        try {
            logger.log(Level.INFO, "Calling getCapabilityResponseFromSource()");
            return (CapabilityResponseType) wrapper.getUnmarshaller().unmarshal(source);
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized CapabilityResponseType createResponse(InputStream inputStream) throws CapabilityDemoFactoryException {
        try {
            logger.log(Level.INFO, "Calling getCapabilityResponseFromInputStream()");
            return (CapabilityResponseType) wrapper.getUnmarshaller().unmarshal(inputStream);
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized Document createResponseDocument(CapabilityResponseType response) throws CapabilityDemoFactoryException {
        logger.log(Level.INFO, "Calling getCapabilityResponseToDocument() on ResponseID: {0}",
                response.getCapabilityResponseID());
        return createResponseDocument(response, Boolean.FALSE);
    }

    public synchronized Document createResponseFragmentDocument(CapabilityResponseType response) throws CapabilityDemoFactoryException {
        logger.log(Level.INFO, "Calling getFragmentCapabilityResponseToDocument() on ResponseID: {0}",
                response.getCapabilityResponseID());
        return createResponseDocument(response, Boolean.TRUE);
    }

    private synchronized Document createResponseDocument(CapabilityResponseType response, boolean fragment) throws CapabilityDemoFactoryException {
        try {
            Document capabilityDocument = DOMUtils.newDocument();
            wrapper.getMarshaller().setProperty(Marshaller.JAXB_FRAGMENT, fragment);
            wrapper.getMarshaller().marshal(response, capabilityDocument);
            return capabilityDocument;
        } catch (ParserConfigurationException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized CapabilityResponseType createResponse(String serverStatus, String requestID)
            throws CapabilityDemoFactoryException {

        logger.log(Level.INFO, "Calling createCapabilityResponse() ");

        //Create a new Capability Response Object Factory
        CapabilityResponseType response = responseFactory.createCapabilityResponseType();

        //Set the version
        response.setVersion(String.valueOf(CAPABILITY_RESPONSE_VERSION));

        //Set the response istant
        Date now = new Date();
        XMLGregorianCalendar issueIstant = null;
        try {
            issueIstant = CalendarUtils.newXmlGregorianCalendarFromDateAndTime(now, now);
        } catch (DatatypeConfigurationException ex) {
            throw new CapabilityDemoFactoryException("Error while creating Issue istant Date.", ex);
        }
        XMLGregorianCalendar issueIstantUTC = issueIstant.normalize();
        response.setResponseIstant(issueIstantUTC);

        //Set the Capability Response ID
        try {
            response.setCapabilityResponseID(
                    IdentifierGenerator.generateIdentifier());
        } catch (NoSuchAlgorithmException ex) {
            throw new CapabilityDemoFactoryException("Error while generating identifier.", ex);
        }

        //Set the received request id
        response.setRequestID(requestID);

        //Set the server status
        response.setStatus(serverStatus);

        //return the result
        return response;
    }
}
